package com.github.thread.threadLocal;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 *  TransmittableThreadLocal正确使用
 *  https://www.cnblogs.com/JohnsonLiu/p/15733680.html
 */
class TransmittableThreadLocalCase {
    private static final ThreadLocal<Map<Integer, Integer>> THREAD_LOCAL = new TransmittableThreadLocal() {
        /**
         * 如果使用的是TtlExecutors装饰的线程池或者TtlRunnable、TtlCallable装饰的任务
         * 重写copy方法且重新赋值给新的LinkedHashMap，不然会导致父子线程都是持有同一个引用，只要有修改取值都会变化。引用值线程不安全
         * parentValue是父线程执行子任务那个时刻的快照值，后续父线程再次set值也不会影响子线程get，因为已经不是同一个引用
         * @param parentValue
         * @return
         */
        @Override
        public Object copy(Object parentValue) {
            if (parentValue instanceof Map) {
                System.out.println("copy");
                return new LinkedHashMap<Integer, Integer>((Map) parentValue);
            }
            return null;
        }

        /**
         * 如果使用普通线程池执行异步任务，重写childValue即可实现子线程获取的是父线程执行任务那个时刻的快照值，重新赋值给新的LinkedHashMap，父线程修改不会影响子线程（非共享）
         * 但是如果使用的是TtlExecutors装饰的线程池或者TtlRunnable、TtlCallable装饰的任务，此时就会变成引用共享，必须得重写copy方法才能实现非共享
         * @param parentValue
         * @return
         */
        @Override
        protected Object childValue(Object parentValue) {
            if (parentValue instanceof Map) {
                System.out.println("childValue");
                return new LinkedHashMap<Integer, Integer>((Map) parentValue);
            }
            return null;
        }

        /**
         * 初始化,每次get时都会进行初始化
         * @return
         */
        @Override
        protected Object initialValue() {
            System.out.println("initialValue");
            return new LinkedHashMap<Integer, Integer>();
        }
    };

    private static final ExecutorService executorService = TtlExecutors.getTtlExecutorService(new ThreadPoolExecutor(1, 1, 1000, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(1000)));
//    private static final Executor executor = new ThreadPoolExecutor(1, 1, 1000, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(1000));

    static int i = 0;

    public static void main(String[] args) {
        THREAD_LOCAL.get().put(++i, i);

        executorService.execute(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 子线程(rm之前)：" + THREAD_LOCAL.get());
        });

        THREAD_LOCAL.get().put(++i, i);
        THREAD_LOCAL.remove();

        executorService.execute(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 子线程(rm之后)：" + THREAD_LOCAL.get());
        });

        executorService.shutdown();

    }
}